﻿//////////////////////////////////////////////////////////////////////////////
//
// Copyright © 1998-2024 Glodon Company Limited.
//
// Licensed under the MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the “Software”),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////
#include "IUiView.h"
#include "IModelView.h"
#include "UiViewUtils.h"
#include "UIRenderUtils.h"
#include "IGraphicsCurve3d.h"
#include "IGraphicsElementShape.h"
#include "ILine3d.h"
#include "IEdge.h"
#include "ICurve3d.h"
#include "ITrimmedCurve.h"
#include "IGraphicsNodeReference.h"
#include "ISelection.h"
#include "IGenericElement.h"
#include "IDocument.h"
#include "IElementModelShape.h"
#include "GcmpPreviewHelper.h"
#include "IInstance.h"

// Render Temp Line
#include "GcmpBuiltInCategoryUniIdentities.h"
#include "IGraphicsStyleManager.h"
#include "IGraphicsBRepBody.h"
#include "IGraphicsBRepEdge.h"
#include "GbmpGraphicsNodeUtils.h"
#include "IElementViewSpecificShapeComponent.h"
#include "Vector3dUtils.h"

#include "UiDocumentViewUtils.h"
#include "IElementBasicInformation.h"
#include "IDrawingTable.h"
#include "EnableCompileWarning_The_LAST_IncludeInCpp.h"


using namespace gcmp;

PreviewHelper::PreviewHelper(const IUiView* pUiView, const std::vector<IElement*> &elements)
    :m_wpUiView(pUiView)
{
    ResetPreview(pUiView, elements);
}

PreviewHelper::~PreviewHelper()
{

}

void PreviewHelper::ResetPreview(const IUiView* pUiView, const std::vector<IElement *> &elements)
{
    IModelView* pModelView = pUiView->GetModelView();
    DBG_WARN_AND_RETURN_VOID_UNLESS(pModelView, L"pModelView为空",L"GDMPLab",L"2024-03-30");

    IDocument* pDoc = pModelView->GetDocument();
    DBG_WARN_AND_RETURN_VOID_UNLESS(pDoc != nullptr, L"无效的文档",L"GDMPLab",L"2024-03-30");
    Box3d totalbox;

    m_preViewObjects.clear();
    if (!elements.empty())
    {
        for (auto it = elements.begin(); it != elements.end(); it++)
        {
            IElement * pElement = *it;

            GetGraphicNodeFromElement(pElement, pModelView, pDoc);

        }
    }
    else
    {
        std::vector<IElement*> operationElements;
        GetSelectedElements(operationElements);
        FOR_EACH(item, operationElements)
        {
            IElement* pElement = pDoc->GetElement(item->GetElementId());
            
            GetGraphicNodeFromElement(pElement, pModelView, pDoc);
        }

    }

    //拿第一个非空包围盒
    if (!m_preViewObjects.empty())
    {
        FOR_EACH(node, m_preViewObjects)
        {
            if (nullptr != node)
            {
                GetBox(node.get(), totalbox);
                if (!totalbox.IsEmpty())
                    break;
            }

        }       
    }

    //一个非空包围盒都拿不到就结束绘制包围盒
    if (totalbox.IsEmpty())
        return;

    FOR_EACH(po, m_preViewObjects)
    {
        GetBox(po.get(), totalbox);
    }



    if (!m_preViewObjects.empty())
    {
        Vector3d corner[8];        
        AddBoxLines(pDoc, totalbox,m_preViewObjects);
    }


}

void PreviewHelper::GetGraphicNodeFromElement(IElement * pElement, IModelView * pModelView, IDocument* pDoc)
{
    if (nullptr == pElement)
        return;

    IElementModelShape * modelshape = pElement->GetElementModelShape();
    if (nullptr == modelshape)
        return;

    const IGraphicsElementShape * shape = modelshape->GetGraphicsElementShape();
    if (nullptr == shape)
    {
        if (const IElementViewSpecificShapeComponent * pIElementViewSpecificShapeComponent = pElement->GetViewSpecificShapeComponent())
        {
            OwnerPtr<IGraphicsElementShape> opIGraphicsElementShape =
                pIElementViewSpecificShapeComponent->CreateViewSpecificElementShape(pModelView);
            if (nullptr == opIGraphicsElementShape)
                return;

            bool isclonable = opIGraphicsElementShape->IsCloneable();  
            if (!isclonable)
            {
                std::vector<const IGraphicsNode *> children = opIGraphicsElementShape->GetAllChildren();
                FOR_EACH(node, children)
                {
                    bool isclonable = node->IsCloneable();
                    if (isclonable)
                    {
                        m_preViewObjects.emplace_back(node->Clone());
                    }
                    else
                    {
                        GetClonableSubGraphicNode(node);                        
                    }
                }
            }
            else              
                m_preViewObjects.emplace_back(opIGraphicsElementShape->Clone());
        }
    }
    else
    {
        IInstance* pInstance = quick_cast<IInstance>(pElement);
        if (pInstance && (pInstance->GetSuperInstanceId().IsValid() || !pInstance->GetSubInstanceIds().empty()))
        {
            std::vector<OwnerPtr<IGraphicsBRepBody>> allBodies = GbmpGraphicsNodeUtils::GetAllGrepBodys(pDoc, shape);
            for (auto& opBody : allBodies)
            {
                DBG_WARN_AND_RETURN_VOID_UNLESS(opBody, L"opBody is nullptr!",L"GDMPLab",L"2024-03-30");
                std::vector<const IGraphicsBRepEdge *> currentEdges = opBody->GetEdges();
                FOR_EACH(edge, currentEdges)
                {
                    if (!edge)
                        continue;
                    const IEdge * gmedge = edge->GetGmEdge();
                    if (!gmedge)
                        continue;
                    const ITrimmedCurve* trimmedcurve = gmedge->GetTrimmedCurve();
                    if (!trimmedcurve)
                        continue;
                    OwnerPtr<ICurve3d> curve3d = trimmedcurve->GetUnderlyingCurve();
                    OwnerPtr<IGraphicsCurve3d> ownercurve3d = IGraphicsCurve3d::Create(curve3d.get());

                    if (curve3d)
                        m_preViewObjects.emplace_back(TransferOwnership(ownercurve3d));
                }
            }

            return;
        }
        
        OwnerPtr<IGraphicsBRepBody> body = GbmpGraphicsNodeUtils::GetGRepBody(pDoc, shape);
        if (nullptr == body.get())
        {
            std::vector<const IGraphicsNode *> children = shape->GetAllChildren();
            FOR_EACH(node, children)
            {
                DBG_WARN_AND_RETURN_VOID_UNLESS(node, L"node is nullptr!",L"GDMPLab",L"2024-03-30");
                bool isclonable = node->IsCloneable();
                if (isclonable)
                {
                    m_preViewObjects.emplace_back(node->Clone());
                }
                else
                {
                    GetClonableSubGraphicNode(node);
                }
            }
            return;
        }

        std::vector<const IGraphicsBRepEdge *> currentEdges = body->GetEdges();
        FOR_EACH(edge, currentEdges)
        {
            if (!edge)
                continue;
            const IEdge * gmedge = edge->GetGmEdge();
            if (!gmedge)
                continue;
            const ITrimmedCurve* trimmedcurve = gmedge->GetTrimmedCurve();
            if (!trimmedcurve)
                continue;
            OwnerPtr<ICurve3d> curve3d = trimmedcurve->GetUnderlyingCurve();
            OwnerPtr<IGraphicsCurve3d> ownercurve3d = IGraphicsCurve3d::Create(curve3d.get());

            if (curve3d)
                m_preViewObjects.emplace_back(TransferOwnership(ownercurve3d));
        }
    }

}


OwnerPtr<IGraphicsElementShape> PreviewHelper::GetMoveTempGRep(const Vector3d& offsetDist)
{
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(m_wpUiView, L"m_wpUiView为空",L"GDMPLab",L"2024-03-30");
    const IModelView* pModelView = m_wpUiView->GetModelView();
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pModelView, L"pModelView为空",L"GDMPLab",L"2024-03-30");
    const IDocument* pDoc = pModelView->GetDocument();
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pDoc, L"pDoc为空",L"GDMPLab",L"2024-03-30");

    const IGraphicsStyleManager* pStyleManager = pDoc->GetGraphicsStyleManager();
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pStyleManager, L"pDoc->GetGraphicsStyleManager() 返回空指针",L"GDMPLab",L"2024-03-30");
    OwnerPtr<IGraphicsElementShape> m_tempGRep = IGraphicsElementShape::Create(GraphicsRenderLayer::TransientObject);
    m_tempGRep->SetGraphicsStyleId(pStyleManager->GetGraphicsStyleIdByCategoryUid(BuiltInCategoryUniIdentities::BICU_TEMP_STYLE));

    OwnerPtr<IGraphicsBRepBody> body = GbmpGraphicsNodeUtils::GetGRepBody(pDoc, m_tempGRep.get());
    Matrix4d mat;
    mat.MakeTranslate(offsetDist);

    FOR_EACH(element, m_preViewObjects)
    {
        if (nullptr == element)
            continue;

        OwnerPtr<IGraphicsNode> node = element->Clone();

        node->Transform(mat);
        m_tempGRep->AddChild(TransferOwnership(node));
    }


    return  TransferOwnership(m_tempGRep);
}

void PreviewHelper::GetClonableSubGraphicNode(const IGraphicsNode * pGraphicNode)
{
    if (nullptr == pGraphicNode)
    {
        return;
    }

    const IGraphicsElementShape * pGriaphicsElementShape = quick_cast<IGraphicsElementShape>(pGraphicNode);
    if (nullptr == pGriaphicsElementShape)
        return;
    std::vector<const IGraphicsNode *> children = pGriaphicsElementShape->GetAllChildren();

    FOR_EACH(node, children)
    {

        bool isclonable = node->IsCloneable();
        if (isclonable)
        {
            m_preViewObjects.emplace_back(node->Clone());
        }
        else
        {
            GetClonableSubGraphicNode(node);
        }
    }

}

void PreviewHelper::GetBox(const IGraphicsNode * pGraphicNode, Box3d & box)
{
    Box3d objbox = pGraphicNode->GetBox();

    if(objbox.IsEmpty())
    {
        const IGraphicsElementShape * pges = quick_cast<IGraphicsElementShape>(pGraphicNode);
        if (nullptr == pges)
            return;

        std::vector<const IGraphicsNode *> children = pges->GetAllChildren();
        FOR_EACH(node, children)
        {
            GetBox(node, box);
        }
    }
    else
        box.MergeBox(objbox);
}

void PreviewHelper::AddBoxLines(IDocument* pDoc, Box3d &totalbox, std::vector<OwnerPtr<IGraphicsNode>> & preViewObjects)
{
    if (nullptr == pDoc)
        return;

    if (totalbox.IsEmpty())
        return;

    const Vector3d & minpnt = totalbox.GetMinPoint();
    const Vector3d & maxpnt = totalbox.GetMaxPoint();

    //当包围盒的minpnt和maxpnt距离在长度精度下为0时，不绘制包围盒的线（比如单个点图形节点，避免Vector3d除以0的问题）
    if (MathUtils::IsZero(Vector3dUtils::Distance(minpnt, maxpnt), Constants::LENGTH_EPS))
        return;

    Vector3d corner[8];

    corner[0].Set(minpnt.X(), minpnt.Y(), minpnt.Z());
    corner[1].Set(maxpnt.X(), minpnt.Y(), minpnt.Z());    
    corner[2].Set(maxpnt.X(), maxpnt.Y(), minpnt.Z());
    corner[3].Set(minpnt.X(), maxpnt.Y(), minpnt.Z());

    corner[4].Set(minpnt.X(), minpnt.Y(), maxpnt.Z());
    corner[5].Set(maxpnt.X(), minpnt.Y(), maxpnt.Z());    
    corner[6].Set(maxpnt.X(), maxpnt.Y(), maxpnt.Z());
    corner[7].Set(minpnt.X(), maxpnt.Y(), maxpnt.Z());

    double gap = UiViewUtility::ComputeWorldWidthFromPixel(m_wpUiView.Get(), HELPER_RECT_GAP) / 2.0;
    Vector3d center = totalbox.GetCenter();
    FOR_EACH(v, corner)
    {
        Vector3d dir = (v - center) / (v - center).Normalize();
        if (dir.GetX() > 0)
            v.SetX(v.GetX() + gap);
        else
            v.SetX(v.GetX() - gap);

        if (dir.GetY() > 0)
            v.SetY(gap + v.GetY());
        else
            v.SetY(v.GetY() - gap);

        if (dir.GetZ() > 0)
            v.SetZ(gap + v.GetZ());
        else
            v.SetZ(v.GetZ() - gap);

    }
    const IGraphicsStyleManager* pStyleManager = pDoc->GetGraphicsStyleManager();
    DBG_WARN_AND_RETURN_VOID_UNLESS(pStyleManager, L"pDoc->GetGraphicsStyleManager() 返回空指针",L"GDMPLab",L"2024-03-30");
    OwnerPtr<IGraphicsNode> boxline1 = IGraphicsCurve3d::Create(ILine3d::Create(corner[0], corner[1]).get());
    boxline1->SetGraphicsStyleId(pStyleManager->GetGraphicsStyleIdByCategoryUid(BuiltInCategoryUniIdentities::BICU_REFERENCED_CURVE));
    OwnerPtr<IGraphicsNode> boxline2 = IGraphicsCurve3d::Create(ILine3d::Create(corner[0], corner[3]).get());
    boxline2->SetGraphicsStyleId(pStyleManager->GetGraphicsStyleIdByCategoryUid(BuiltInCategoryUniIdentities::BICU_REFERENCED_CURVE));
    OwnerPtr<IGraphicsNode> boxline3 = IGraphicsCurve3d::Create(ILine3d::Create(corner[0], corner[4]).get());
    boxline3->SetGraphicsStyleId(pStyleManager->GetGraphicsStyleIdByCategoryUid(BuiltInCategoryUniIdentities::BICU_REFERENCED_CURVE));
    OwnerPtr<IGraphicsNode> boxline4 = IGraphicsCurve3d::Create(ILine3d::Create(corner[2], corner[1]).get());
    boxline4->SetGraphicsStyleId(pStyleManager->GetGraphicsStyleIdByCategoryUid(BuiltInCategoryUniIdentities::BICU_REFERENCED_CURVE));
    OwnerPtr<IGraphicsNode> boxline5 = IGraphicsCurve3d::Create(ILine3d::Create(corner[2], corner[3]).get());
    boxline5->SetGraphicsStyleId(pStyleManager->GetGraphicsStyleIdByCategoryUid(BuiltInCategoryUniIdentities::BICU_REFERENCED_CURVE));
    OwnerPtr<IGraphicsNode> boxline6 = IGraphicsCurve3d::Create(ILine3d::Create(corner[2], corner[6]).get());
    boxline6->SetGraphicsStyleId(pStyleManager->GetGraphicsStyleIdByCategoryUid(BuiltInCategoryUniIdentities::BICU_REFERENCED_CURVE));
    OwnerPtr<IGraphicsNode> boxline7 = IGraphicsCurve3d::Create(ILine3d::Create(corner[1], corner[5]).get());
    boxline7->SetGraphicsStyleId(pStyleManager->GetGraphicsStyleIdByCategoryUid(BuiltInCategoryUniIdentities::BICU_REFERENCED_CURVE));
    OwnerPtr<IGraphicsNode> boxline8 = IGraphicsCurve3d::Create(ILine3d::Create(corner[3], corner[7]).get());
    boxline8->SetGraphicsStyleId(pStyleManager->GetGraphicsStyleIdByCategoryUid(BuiltInCategoryUniIdentities::BICU_REFERENCED_CURVE));
    OwnerPtr<IGraphicsNode> boxline9 = IGraphicsCurve3d::Create(ILine3d::Create(corner[4], corner[5]).get());
    boxline9->SetGraphicsStyleId(pStyleManager->GetGraphicsStyleIdByCategoryUid(BuiltInCategoryUniIdentities::BICU_REFERENCED_CURVE));
    OwnerPtr<IGraphicsNode> boxline10 = IGraphicsCurve3d::Create(ILine3d::Create(corner[5], corner[6]).get());
    boxline10->SetGraphicsStyleId(pStyleManager->GetGraphicsStyleIdByCategoryUid(BuiltInCategoryUniIdentities::BICU_REFERENCED_CURVE));
    OwnerPtr<IGraphicsNode> boxline11 = IGraphicsCurve3d::Create(ILine3d::Create(corner[6], corner[7]).get());
    boxline11->SetGraphicsStyleId(pStyleManager->GetGraphicsStyleIdByCategoryUid(BuiltInCategoryUniIdentities::BICU_REFERENCED_CURVE));
    OwnerPtr<IGraphicsNode> boxline12 = IGraphicsCurve3d::Create(ILine3d::Create(corner[7], corner[4]).get());
    boxline12->SetGraphicsStyleId(pStyleManager->GetGraphicsStyleIdByCategoryUid(BuiltInCategoryUniIdentities::BICU_REFERENCED_CURVE));

    m_preViewObjects.emplace_back(TransferOwnership(boxline1));
    m_preViewObjects.emplace_back(TransferOwnership(boxline2));
    m_preViewObjects.emplace_back(TransferOwnership(boxline3));
    m_preViewObjects.emplace_back(TransferOwnership(boxline4));
    m_preViewObjects.emplace_back(TransferOwnership(boxline5));
    m_preViewObjects.emplace_back(TransferOwnership(boxline6));
    m_preViewObjects.emplace_back(TransferOwnership(boxline7));
    m_preViewObjects.emplace_back(TransferOwnership(boxline8));
    m_preViewObjects.emplace_back(TransferOwnership(boxline9));
    m_preViewObjects.emplace_back(TransferOwnership(boxline10));
    m_preViewObjects.emplace_back(TransferOwnership(boxline11));
    m_preViewObjects.emplace_back(TransferOwnership(boxline12));
}


void PreviewHelper::GetSelectedElements(std::vector<IElement*> & operationElements)
{
    IDocument* pDoc = UiDocumentViewUtils::GetCurrentDocument();
    DBG_WARN_AND_RETURN_VOID_UNLESS(pDoc, L"pDoc为空",L"GDMPLab",L"2024-03-30");

    ISelection* pSelection = ISelection::Get();
    DBG_WARN_AND_RETURN_VOID_UNLESS(pSelection, L"pSelection为空",L"GDMPLab",L"2024-03-30");

    const GraphicsNodeReferenceOwnerPtrSet& tempselectedItems = pSelection->GetGraphicsNodeReferences();
    FOR_EACH(it, tempselectedItems)
    {
        const ElementId elementId = it->GetElementId();
        IElement *pElement = pDoc->GetElement(elementId);
        if (pElement != nullptr)
        {
            IElementBasicInformation* pBasicInformation = pElement->GetBasicInformation();
            if (pBasicInformation)
            {
                if (pBasicInformation->GetCategoryUid() != BuiltInCategoryUniIdentities::BICU_DRAWING_TABLE_ELEMENT && pBasicInformation->GetClassficationUid() != GuidUtils::FromString(L"{EDBC1DCC-7BC6-4EE1-BD90-0880B2E3E5C8}"))
                    operationElements.emplace_back(pElement);
                else
                {
                    IGenericElement* pGenericElement = quick_cast<IGenericElement>(pElement);
                    if (pGenericElement)
                    {
                        if (IDrawingTable* pTableElement = quick_cast<IDrawingTable>(pGenericElement->GetExternalObject()))
                        {
                            operationElements.emplace_back(pElement);
                            std::set<ElementId> items = pTableElement->GetTableItemIds();
                            FOR_EACH(item, items)
                            {
                                IElement* pTableItem = pDoc->GetElement(item);
                                if(pTableItem)
                                    operationElements.emplace_back(pTableItem);
                            }
                        }
                    }
                }
            }
        }
    }
}